package repository

import (
	"context"
	"encoding/csv"
	"fmt"
	"gitee.com/xiao_hange/go-admin-file/file/domain"
	"gitee.com/xiao_hange/go-admin-file/file/repository/dao"
	"gitee.com/xiao_hange/go-admin-file/file/service/file"
	"gitee.com/xiao_hange/go-admin-file/file/service/mail"
	"gitee.com/xiao_hange/go-admin-pkg/pkg/tool"
	uuid "github.com/lithammer/shortuuid/v4"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/trace"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type FileRepository interface {
	List(ctx context.Context, page, limit int, types uint8) ([]domain.File, int, error)
	Export(ctx context.Context, page, limit, export int) ([]domain.Data, int, error)
	Add(ctx context.Context, file domain.File) error
	Delete(ctx context.Context, id int64) error
	UpdateNameById(ctx context.Context, id int64, name string) error
	SendMailForCsv(ctx context.Context, mail string) error
}

var (
	ErrDataTooMuch  = dao.ErrDataTooMuch
	downloadDirPath = "../download/csv/"
	cosFilePath     = "go/file/"
	cdnUrl          = "http://cdn.jdc51.com/"
	parseUrl        = "https://sxq-1318882435.cos.ap-beijing.myqcloud.com/"
)

type fileRepository struct {
	fileSvc file.Service
	dao     dao.FileDao
	tracer  trace.Tracer
}

func NewFileRepository(dao dao.FileDao, fileSvc file.Service) FileRepository {
	return &fileRepository{
		dao:     dao,
		fileSvc: fileSvc,
		tracer:  otel.Tracer("File服务-Repository层"),
	}
}

func (f *fileRepository) List(ctx context.Context, page, limit int, types uint8) ([]domain.File, int, error) {
	lists, total, err := f.dao.List(ctx, page, limit, types)
	result := make([]domain.File, len(lists))
	for i, list := range lists {
		var dtoFile domain.File
		tool.ConvertDAOToDTO(list, &dtoFile)
		dtoFile.Image = fmt.Sprintf("%s%s", cdnUrl, dtoFile.Image)
		result[i] = dtoFile
	}
	return result, total, err
}

func (f *fileRepository) Add(ctx context.Context, file domain.File) error {
	var daoFile dao.File
	tool.ConvertDTOToDAO(file, &daoFile)
	return f.dao.Insert(ctx, daoFile)
}

func (f *fileRepository) UpdateNameById(ctx context.Context, id int64, name string) error {
	return f.dao.UpdateName(ctx, id, name)
}
func (f *fileRepository) Delete(ctx context.Context, id int64) error {
	return f.dao.Delete(ctx, id)
}

func (f *fileRepository) Export(ctx context.Context, page, limit, export int) ([]domain.Data, int, error) {
	spanCtx, span := f.tracer.Start(ctx, "File服务-Repository层: Export")
	defer span.End()
	lists, total, err := f.dao.Export(spanCtx, page, limit, export)
	result := make([]domain.Data, len(lists))
	for i, list := range lists {
		var dtoData domain.Data
		tool.ConvertDAOToDTO(list, &dtoData)
		result[i] = dtoData
	}
	return result, total, err
}

//func (f *fileRepository) SendMailForCsv(ctx context.Context, mail string) error {
//	datas, err := f.dao.ExportData(ctx, 1, 1000)
//	if err != nil {
//		return err
//	}
//	rows := make([][]string, 0, len(datas))
//	for _, data := range datas {
//		row := f.constructCSVRow(data)
//		rows = append(rows, row)
//	}
//	uuId := uuid.New()
//	header := []string{"ID", "标题", "内容", "创建时间"}
//
//	err = f.writeCSV(fmt.Sprintf("%s.csv", uuId), header, rows, mail)
//	return err
//}

//	func (f *fileRepository) SendMailForCsv(ctx context.Context, mail string) error {
//		pageSize := 1000
//		header := []string{"ID", "标题", "内容", "更新时间", "创建时间"}
//		uuId := uuid.New()
//		page := 1
//		for {
//			datas, err := f.dao.ExportData(ctx, page, pageSize)
//			if err != nil {
//				break
//			}
//			if len(datas) == 0 {
//				break
//			}
//			for _, data := range datas {
//				row := f.constructCSVRow(data)
//				filePath,_ = f.writeCSV(fmt.Sprintf("%s.csv", uuId), header, row, mail)
//			}
//			page++
//		}
//		return nil
//	}
func (f *fileRepository) SendMailForCsv(ctx context.Context, mailStr string) error {
	pageSize := 1000
	header := []string{"ID", "标题", "内容", "更新时间", "创建时间"}
	uuId := uuid.New()
	page := 1
	var allRows [][]string
	for {
		datas, err := f.dao.ExportData(ctx, page, pageSize)
		if err != nil {
			break
		}
		if len(datas) == 0 {
			break
		}
		for _, data := range datas {
			row := f.constructCSVRow(data)
			allRows = append(allRows, row)
		}
		page++
	}
	var csvContentBuilder strings.Builder
	writer := csv.NewWriter(&csvContentBuilder)
	if err := writer.Write(header); err != nil {
		return err
	}
	if err := writer.WriteAll(allRows); err != nil {
		return err
	}
	writer.Flush()
	csvContent := csvContentBuilder.String()
	objectKey := fmt.Sprintf("%s%s.csv", cosFilePath, uuId)
	_ = f.writeCSVToCOS(objectKey, csvContent)
	err := mail.SendMailForCos(mailStr, fmt.Sprintf("%s%s", parseUrl, objectKey))
	return err
}

func (f *fileRepository) constructCSVRow(data dao.Data) []string {
	return []string{
		strconv.FormatInt(data.ID, 10),
		data.Title,
		data.Content,
		tool.FormatUnixTime(data.UpdateTime),
		tool.FormatUnixTime(data.CreateTime),
	}
}

// 文件下载到本地
func (f *fileRepository) writeCSV(fileName string, header []string, rows []string, mailStr string) (string, error) {

	if err := os.MkdirAll(downloadDirPath, os.ModePerm); err != nil {
		return "", err
	}

	filePath := filepath.Join(downloadDirPath, fileName)
	file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		return "", err
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	// 如果文件为空，写入 CSV 表头
	fileInfo, _ := file.Stat()
	if fileInfo.Size() == 0 {
		if err = writer.Write(header); err != nil {
			return "", err
		}
	}

	// 写入 CSV 数据行
	if err = writer.Write(rows); err != nil {
		return "", err
	}

	return filePath, nil
}

// 文件上传到COS or OSS
func (f *fileRepository) writeCSVToCOS(objectKey string, csvContent string) error {
	// 创建管道，将生成的 CSV 内容写入管道
	pr, pw := io.Pipe()
	go func() {
		defer pw.Close()
		_, _ = io.WriteString(pw, csvContent)
	}()
	// 执行上传
	err := f.fileSvc.UploadCsv(context.Background(), objectKey, pr)
	return err
}
